/**
	lcz 20201015
*/
# gcc compiler allowed the assembly include c language header file
# so include the header file and use the var now!!

#  if_no with_err_code_, push a 0 as error code and it symbol no error code
#  if_have error code, Hardware will push error code automatic
#include "os.cfg.h"
# #include "cpu/syscall.h"



.macro 	exception_handler name num with_err_code
	.text
	.extern do_handler_\name
	.global exception_handler_\name
exception_handler_\name:
	.if \with_err_code == 0
		push $0
	.endif
	push $\num

	pusha
	push %ds
	push %es
	push %fs
	push %gs


	# make a paramter for function do_handler_unknown by handle
	# and it is a point to registers whose push registers, it will be
	# analyzed by gdb as a struct exception_frame_t which defined in irq.h

	push %esp
	call do_handler_\name
	pop %esp

	pop %gs
	pop %fs
	pop %es
	pop %ds
	popa

	add $(2*4),%esp
	iret

.endm



 	.text
	.global _start
	.global gdt_reload
	.extern kernel_init
	.extern init_main
_start:
	# create a stack call by kernel_init 
	# like C language

	push %ebp
	mov %esp,%ebp
	mov 0x8(%ebp),%eax
	# eax storaged the boot_info address, it is a parameter
	push %eax
	call kernel_init

	# also
	# mov 0x4(%esp),%eax
	# because function kernel_init don't recovery, so not add instruction of pop %ebp
	
	# after kernel_init ,we start init_main module
	# now let us reload the registers and gdt table
	# jmp instruction modify the cs register
	jmp $KERNEL_SELECTOR_CS,$gdt_reload
gdt_reload:
	# modify data segment
	mov $KERNEL_SELECTOR_DS,%ax
	mov %ax,%ds
	mov %ax,%ss
	mov %ax,%es
	mov %ax,%fs
	mov %ax,%gs
	# setting stack 
	mov $(stack+KERNEL_STACK_SIZE),%esp
	jmp init_main

	.bss
	.comm stack,KERNEL_STACK_SIZE
	
	# "stack" point the base address for stack
	

	.text
	.global exception_handler_unknown
	.extern do_handler_unknown

# use your macro definition
# 定义系统默认的异常
exception_handler unknown, -1, 0
exception_handler divider,0,0
exception_handler Debug, 1, 0
exception_handler NMI, 2, 0
exception_handler breakpoint, 3, 0
exception_handler overflow, 4, 0
exception_handler bound_range, 5, 0
exception_handler invalid_opcode, 6, 0
exception_handler device_unavailable, 7, 0
exception_handler double_fault, 8, 1
exception_handler invalid_tss, 10, 1
exception_handler segment_not_present, 11, 1
exception_handler stack_segment_fault, 12, 1
exception_handler general_protection, 13, 1
exception_handler page_fault, 14, 1
exception_handler fpu_error, 16, 0
exception_handler alignment_check, 17, 1
exception_handler machine_check, 18, 0
exception_handler smd_exception, 19, 0
exception_handler virtual_exception, 20, 0

# time interrupt
exception_handler time, 0x20, 0
# keyboard
exception_handler kbd, 0x21, 0
exception_handler ide_primary, 0x2e, 0


# simple_switch(&from,to)
	.text 
	.global simple_switch
simple_switch:
	# 8(%esp),%edx to stack pointe
	mov 4(%esp),%eax
	mov 8(%esp),%edx
	push %ebp
	push %ebx
	push %esi
	push %edi
	# esp write into memory
	mov %esp,(%eax)

	mov %edx,%esp
	pop %edi
	pop %esi
	pop %ebx
	pop %ebp
	ret

	
	.global exception_handler_syscall
	.extern do_handler_syscall
exception_handler_syscall:
	# 现在手工压入写寄存器
	pusha
	push %ds
	push %es
	push %fs
	push %gs
	pushf
	
	mov %esp,%eax
	push %eax
	# C 处理函数   esp就是参数 指向我们的地址，手动压栈部分其实是为了实现子进程的系统调用。fork
	call do_handler_syscall
	add $4,%esp
	
    popf
	pop %gs
	pop %fs
	pop %es
	pop %ds
	popa

	retf $(5*4)
	# 出栈的时候要跳过参数
	# 远返回 far return 及返回到不同代码段中
	# esp还会调整并返回


